/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2007-2009: ngi-central.org                                  */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements data structures for moments calculations.


#ifndef __sgl_unnormalized_moments_h__
#define __sgl_unnormalized_moments_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/real_concept.h>
#include <boost/operators.hpp>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* unnormalized_moments                                                       */
/*                                                                            */
/******************************************************************************/

template <
	/// The type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), but 
	//! any other numeric type is also suitable.
	typename T=SGL_FPTYPE>
/// Class to hold raw moments of the zeroth up to the third order.
//!
//! Unnormalized moments are not invariant with respect to translation, scaling 
//! and rotation.
//!
//! The following operations are implemented:
//! operator== : comparison for equality, directly implemented.
//! operator != : comparison for inequality, implemented through 
//! boost::equality_comaparable.
class unnormalized_moments
	: boost::equality_comparable<sgl::unnormalized_moments<T>, 
		sgl::unnormalized_moments<T>
	>
{
	// Only real types qualify for type T. If this line triggers an error,
	// type T is not a real type.
	BOOST_CONCEPT_ASSERT((sgl::real_concept<T>));

public:
	/// The value type.
	typedef T value_type;

	/// Default constructor. 
	//!
	//! Zero initializes the moments.
	unnormalized_moments()
		: m00_(T())
		, m10_(T())
		, m01_(T())
		, m20_(T())
		, m11_(T())
		, m02_(T())
		, m30_(T())
		, m21_(T())
		, m12_(T())
		, m03_(T())
	{ }

	/// Standard constructor.
	//!
	//! Initializes the moments.
	unnormalized_moments(
		/// Zeroth order moment.
		T m00, 
		/// First order horizontal moment.
		T m10, 
		/// First order vertical moment.
		T m01,
		/// Second order horizontal moment.
		T m20, 
		/// Second order mixed moment.
		T m11,
		/// Second order vertical moment.
		T m02,
		/// Third order horizontal moment.
		T m30, 
		/// Third order horizontal mixed moment.
		T m21,
		/// Third order vertical mixed moment.
		T m12,
		/// Third order vertical moment.
		T m03)
		: m00_(m00)
		, m10_(m10)
		, m01_(m01)
		, m20_(m20)
		, m11_(m11)
		, m02_(m02)
		, m30_(m30)
		, m21_(m21)
		, m12_(m12)
		, m03_(m03)
	{ }

	/// Constructs a copy.
	unnormalized_moments(
		/// The right hand side.
		sgl::unnormalized_moments<T> const& r)
		: m00_(r.m00_)
		, m10_(r.m10_)
		, m01_(r.m01_)
		, m20_(r.m20_)
		, m11_(r.m11_)
		, m02_(r.m02_)
		, m30_(r.m30_)
		, m21_(r.m21_)
		, m12_(r.m12_)
		, m03_(r.m03_)
	{ }

	/// Copy assignment
	sgl::unnormalized_moments<T> & operator =(
		/// The right hand side.
		sgl::unnormalized_moments<T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges two objects.
	void swap(
		/// The other object.
		sgl::unnormalized_moments<T> & rhs)
	{
		std::swap(m00_, rhs.m00_);
		std::swap(m10_, rhs.m10_);
		std::swap(m01_, rhs.m01_);
		std::swap(m20_, rhs.m20_);
		std::swap(m11_, rhs.m11_);
		std::swap(m02_, rhs.m02_);
		std::swap(m30_, rhs.m30_);
		std::swap(m21_, rhs.m21_);
		std::swap(m12_, rhs.m12_);
		std::swap(m03_, rhs.m03_);
	}

	/// Compare objects for equality.
	//!
	//! This instance is equal to another instance, if its moments are all equal 
	//! to the moments of the other instance.
	//!
	//! Through the use of boost::equality_comparable operator != is also
	//! defined and implemented by calling this operator.
	//!
	//! \return true if equal, false otherwise.
    bool operator ==(
		/// Right hand side.
		sgl::unnormalized_moments<T> const& rhs) const
    {
        return 
			m00_ == rhs.m00_ && 
			m10_ == rhs.m10_ && 
			m01_ == rhs.m01_ &&
			m20_ == rhs.m20_ && 
			m11_ == rhs.m11_ &&
			m02_ == rhs.m02_ && 
			m30_ == rhs.m30_ && 
			m21_ == rhs.m21_ &&
			m12_ == rhs.m12_ &&
			m03_ == rhs.m03_;
    }

	/// Access the zeroth order moment.
	//!
	//! /return Returns a reference to the zeroth order moment.
	T & m00()
	{
		return m00_;
	}

	/// Access the zeroth order moment.
	//!
	//! /return Returns a const reference to the zeroth order moment.
	T const & m00() const
	{
		return m00_;
	}

	/// Access the horizontal first order moment.
	//!
	//! /return Returns a reference to the horizontal first order moment.
	T & m10()
	{
		return m10_;
	}

	/// Access the horizontal first order moment.
	//!
	//! /return Returns a const reference to the horizontal first order moment.
	T const & m10() const
	{
		return m10_;
	}

	/// Access the vertical first order moment.
	//!
	//! /return Returns a reference to the vertical first order moment.
	T & m01()
	{
		return m01_;
	}

	/// Access the vertical first order moment.
	//!
	//! /return Returns a const reference to the vertical first order moment.
	T const & m01() const
	{
		return m01_;
	}

	/// Access the horizontal second order moment.
	//!
	//! /return Returns a reference to the horizontal second order moment.
	T & m20()
	{
		return m20_;
	}

	/// Access the horizontal second order moment.
	//!
	//! /return Returns a const reference to the horizontal second order moment.
	T const & m20() const
	{
		return m20_;
	}

	/// Access the mixed second order moment.
	//!
	//! /return Returns a reference to the mixed second order moment.
	T & m11()
	{
		return m11_;
	}

	/// Access the mixed second order moment.
	//!
	//! /return Returns a const reference to the mixed second order moment.
	T const & m11() const
	{
		return m11_;
	}

	/// Access the vertical second order moment.
	//!
	//! /return Returns a reference to the vertical second order moment.
	T & m02()
	{
		return m02_;
	}

	/// Access the vertical second order moment.
	//!
	//! /return Returns a const reference to the vertical second order moment.
	T const & m02() const
	{
		return m02_;
	}

	/// Access the horizontal third order moment.
	//!
	//! /return Returns a reference to the horizontal third order moment.
	T & m30()
	{
		return m30_;
	}

	/// Access the horizontal third order moment.
	//!
	//! /return Returns a const reference to the horizontal third order moment.
	T const & m30() const
	{
		return m30_;
	}

	/// Access the first (horizontal) mixed third order moment.
	//!
	//! /return Returns a reference to the first (horizontal) mixed third order 
	//! moment.
	T & m21()
	{
		return m21_;
	}

	/// Access the first (horizontal) mixed third order moment.
	//!
	//! /return Returns a const reference to the first (horizontal) mixed third 
	//! order moment.
	T const & m21() const
	{
		return m21_;
	}

	/// Access the second (vertical) mixed third order moment.
	//!
	//! /return Returns a reference to the second (vertical) mixed third order 
	//! moment.
	T & m12()
	{
		return m12_;
	}

	/// Access the second (vertical) mixed third order moment.
	//!
	//! /return Returns a const reference to the second (vertical) mixed third 
	//! order moment.
	T const & m12() const
	{
		return m12_;
	}

	/// Access the vertical third order moment.
	//!
	//! /return Returns a reference to the vertical third order moment.
	T & m03()
	{
		return m03_;
	}

	/// Access the vertical third order moment.
	//!
	//! /return Returns a const reference to the vertical third order moment.
	T const & m03() const
	{
		return m03_;
	}

	/// Calculate normalized moment nm10.
	//!
	//! Calculates normalized first order horizontal moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm10.
	T calculate_nm10() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m10_ / m00_;
	}

	/// Calculate normalized moment nm01.
	//!
	//! Calculates normalized first order vertical moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm01.
	T calculate_nm01() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m01_ / m00_;
	}

	/// Calculate normalized moment nm20.
	//!
	//! Calculates normalized second order horizontal moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm20.
	T calculate_nm20() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m20_ / m00_;
	}

	/// Calculate normalized moment nm11.
	//!
	//! Calculates normalized second order mixed moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm11.
	T calculate_nm11() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m11_ / m00_;
	}

	/// Calculate normalized moment nm02.
	//!
	//! Calculates normalized second order vertical moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm02.
	T calculate_nm02() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m02_ / m00_;
	}

	/// Calculate normalized moment nm30.
	//!
	//! Calculates normalized third order horizontal moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm30.
	T calculate_nm30() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m30_ / m00_;
	}

	/// Calculate normalized moment nm21.
	//!
	//! Calculates normalized third order mixed horizontal moment. The 
	//! normalization will cause a division by zero, when the zeroth order 
	//! moment is equal to zero.
	//!
	//! /return Normalized moment nm21.
	T calculate_nm21() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m21_ / m00_;
	}

	/// Calculate normalized moment nm12.
	//!
	//! Calculates normalized third order mixed vertical moment. The 
	//! normalization will cause a division by zero, when the zeroth order 
	//! moment is equal to zero.
	//!
	//! /return Normalized moment nm12.
	T calculate_nm12() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m12_ / m00_;
	}

	/// Calculate normalized moment nm03.
	//!
	//! Calculates normalized third order vertical moment. The normalization
	//! will cause a division by zero, when the zeroth order moment is equal to 
	//! zero.
	//!
	//! /return Normalized moment nm03.
	T calculate_nm03() const
	{
		SGL_PRECONDITION(m00() != T(0), "division by zero.");

		return m03_ / m00_;
	}

private:
	/// The zeroth order moment.
	T m00_;

	/// The horizontal first order moment.
	T m10_;

	/// The vertical first order moment.
	T m01_;

	/// The horizontal second order moment.
	T m20_;

	/// The mixed second order moment.
	T m11_;

	/// The vertical second order moment.
	T m02_;

	/// The horizontal third order moment.
	T m30_;

	/// The first (horizontal) mixed third order moment.
	T m21_;

	/// The second (vertical) mixed third order moment.
	T m12_;

	/// The vertical third order moment.
	T m03_;
};


} // namespace sgl

#endif // __sgl_unnormalized_moments_h__
